use std::{
    path::{Path, PathBuf},
    str::FromStr,
};

use sha1::{Digest, Sha1};

use chat_core::error::AppError;

use super::ChatFile;

impl ChatFile {
    pub fn new(filename: &str, ws_id: u64, data: &[u8]) -> Self {
        let hash = Sha1::digest(data);
        Self {
            ws_id,
            ext: filename.split('.').last().unwrap_or("txt").to_string(),
            hash: hex::encode(hash),
        }
    }

    pub fn url(&self) -> String {
        format!("/files/{}", self.hash_to_path())
    }

    pub fn path(&self, base_dir: &Path) -> PathBuf {
        base_dir.join(self.hash_to_path())
    }

    pub fn hash_to_path(&self) -> String {
        let (part1, part2) = self.hash.split_at(3);
        let (part2, part3) = part2.split_at(3);
        format!("{}/{}/{}/{}.{}", self.ws_id, part1, part2, part3, self.ext)
    }
}

impl FromStr for ChatFile {
    type Err = AppError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let Some(s) = s.strip_prefix("/files/") else {
            return Err(AppError::ChtFileError("Invalid file path".to_string()));
        };
        let parts: Vec<&str> = s.split('/').collect();
        if parts.len() != 4 {
            return Err(AppError::ChtFileError("File path doe".to_string()));
        }
        let Ok(ws_id) = parts[0].parse::<u64>() else {
            return Err(AppError::ChtFileError(format!(
                "Invalid ws_id {}",
                parts[1]
            )));
        };
        let Some((part3, ext)) = parts[3].split_once('.') else {
            return Err(AppError::ChtFileError(format!("Invalid ext {}", parts[3])));
        };

        let hash = format!("{}{}{}", parts[0], parts[1], part3);
        Ok(Self {
            ws_id,
            ext: ext.to_string(),
            hash,
        })
    }
}

#[cfg(test)]
mod tests {
    use crate::AppConfig;

    use super::*;
    use anyhow::Result;
    #[test]
    fn test_chat_file_new_should_work() -> Result<()> {
        let file = ChatFile::new("test.txt", 1u64, b"hello");
        assert_eq!(file.hash, "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d");
        assert_eq!(file.ext, "txt");
        assert_eq!(
            file.url(),
            "/files/1/aaf/4c6/1ddcc5e8a2dabede0f3b482cd9aea9434d.txt"
        );
        let config = AppConfig::load()?;
        println!("{}", config.server.base_dir.display());
        Ok(())
    }
}
